/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "task_worker.h"
#include "hilog/log.h"

namespace ImageKnifeC {
napi_value TaskWorker::QueueWork(InnerWorker *worker)
{
    napi_status status;
    napi_value promise;
    status = napi_create_promise(env_, &worker->deferred_, &promise);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s create promise failed, napi status:%{public}d (Queue Work)",
                     worker->taskInfo.c_str(), status);
        delete worker;
        return nullptr;
    }

    napi_value resourceName;
    status = napi_create_string_utf8(env_, ("ImageKnife " + worker->taskInfo).c_str(), NAPI_AUTO_LENGTH, &resourceName);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s create resource name failed, napi status:%{public}d (Queue Work)",
                     worker->taskInfo.c_str(), status);
        delete worker;
        return nullptr;
    }

    status = napi_create_async_work(env_, nullptr, resourceName, InnerWorker::ExecuteCallback,
                                    InnerWorker::CompleteCallback, worker, &worker->work);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s create async work failed, napi status:%{public}d (Queue Work)",
                     worker->taskInfo.c_str(), status);
        delete worker;
        return nullptr;
    }

    status = napi_queue_async_work_with_qos(env_, worker->work, worker->qos);
    if (status != napi_ok) {
        napi_delete_async_work(env_, worker->work);
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s queue async work failed, napi status:%{public}d (Queue Work)",
                     worker->taskInfo.c_str(), status);
        delete worker;
        return nullptr;
    }

    return promise;
}

// static
void TaskWorker::CallJsCallback(napi_env env, napi_value jsCallback, void *context, void* data)
{
    InnerWorker *worker = static_cast<InnerWorker *>(data);
    GetInstance()->QueueWork(worker);
}

void TaskWorker::QueueWorkThreadsafe(InnerWorker *worker)
{
    if (threadsafeFunc_ == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s Threadsafe func not ready (Queue Work Threadsafe)", worker->taskInfo.c_str());
        delete worker;
        return;
    }
    
    napi_status status = napi_acquire_threadsafe_function(threadsafeFunc_);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s Acquire threadsafe func Failed, napi status:%{public}d (Queue Work Threadsafe)",
                     worker->taskInfo.c_str(), status);
        delete worker;
        return;
    }

    status = napi_call_threadsafe_function(threadsafeFunc_, worker, napi_tsfn_blocking);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s Call threadsafe function Failed, napi status:%{public}d (Queue Work Threadsafe)",
                     worker->taskInfo.c_str(), status);
        delete worker;
    }
}

napi_value TaskWorker::PushTask(AsyncFunc execute, AsyncComplete complete, void *data,
                        std::string taskInfo, napi_qos_t qos)
{
    if (env_ == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::QueueWork",
                     "%{public}s napi_env is null (Push Task)", taskInfo.c_str());
    }

    InnerWorker *worker = new InnerWorker;
    worker->execute = execute;
    worker->complete = complete;
    worker->data = data;
    worker->qos = qos;
    worker->taskInfo = taskInfo;
    
    if (std::this_thread::get_id() == mainThreadId_) {
        napi_value promise = QueueWork(worker);
        return promise;
    } else {
        QueueWorkThreadsafe(worker);
        // 非主线程不返回promise
        return nullptr;
    }
}

bool TaskWorker::PushTask(AsyncTask execute, AsyncTask complete,
                          std::shared_ptr<ImageKnifeTaskInternal> task, napi_qos_t qos)
{
    if (env_ == nullptr) {
        task->EchoError("napi_env is null (Push Task)");
    }

    InnerWorker *worker = new InnerWorker;
    worker->task = task;
    worker->executeTask = execute;
    worker->completeTask = complete;
    worker->qos = qos;
    worker->taskInfo = ImageKnifeTaskInternal::GetTaskInfo(task->GetImageRequestType(),
                                                   task->GetImageKnifeRequest().get());

    if (std::this_thread::get_id() == mainThreadId_) {
        QueueWork(worker);
        return true;
    } else {
        QueueWorkThreadsafe(worker);
    }

    return false;
}

void TaskWorker::PrepareThreadsafeFunc()
{
    napi_status status;
    napi_value workName;
    status = napi_create_string_utf8(env_, "ImageKnife Napi Thread Safe Worker", NAPI_AUTO_LENGTH, &workName);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::PrepareThreadsafeFunc",
                     "PrepareThreadsafeFunc: Create work string failed, errorCode:%{public}d", status);
        return;
    }

    status = napi_create_threadsafe_function(env_, nullptr, nullptr, workName, 0, 1,
                                             nullptr, nullptr, nullptr, CallJsCallback, &threadsafeFunc_);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "TaskWorker::PrepareThreadsafeFunc",
                     "PrepareThreadsafeFunc: Create Thread Safe function Failed, errorCode:%{public}d", status);
    }
}

} // end of namespace

